package dht.client;

import java.util.LinkedList;
import java.util.Queue;
import java.util.Vector;

import dht.IClientAgent;
import dht.IClientRouteTable;
import dht.ITransactionHandler;
import dht.ITransactionProcessor;
import dht.entity.DataObject;
import dht.entity.Message;
import dht.entity.MessageType;
import dht.entity.NodeInfo;
import dht.socket.SocketHandler;
import dht.socket.TcpConnection;
import dht.table.ClientRouteTableImpl;
import dht.throwable.DhtException;
import dht.util.IdGenerator;

public class ClientAgentImpl implements IClientAgent, ITransactionProcessor {

	protected NodeInfo localInfo;
	protected Queue<SocketHandler> socketQueue;
	protected TcpConnection tcpConnection;
	protected IClientRouteTable routeTable;

	public ClientAgentImpl(NodeInfo localInfo, NodeInfo masterInfo) {
		this.localInfo = localInfo;
		this.socketQueue = new LinkedList<SocketHandler>();
		this.tcpConnection = new TcpConnection(localInfo, socketQueue);
		this.routeTable = new ClientRouteTableImpl(masterInfo);
	}

	@Override
	public void initialize() throws DhtException {
		updateRouteTable();
	}

	@Override
	public void read(String fileName) throws DhtException {
		Message msg = new Message(IdGenerator.genTransactionId(),
				MessageType.READ_REQ, localInfo);
		msg.setFileName(fileName);
		NodeInfo serverInfo = getServer(fileName);
		handleInitiativeTransaction(serverInfo, msg, true);
	}

	@Override
	public void write(DataObject dataObject) throws DhtException {
		Message msg = new Message(IdGenerator.genTransactionId(),
				MessageType.WRITE_REQ, localInfo);
		msg.setFileName(dataObject.getBlockName());
		msg.setDataObject(dataObject);
		NodeInfo serverInfo = getServer(dataObject.getBlockName());
		handleInitiativeTransaction(serverInfo, msg, true);
	}

	@Override
	public void handleInitiativeTransaction(NodeInfo nodeInfo, Message msg,
			boolean isSync) throws DhtException {
		ITransactionHandler transactionHandler = new ClientTransactionHandlerImpl(
				localInfo, nodeInfo, this, tcpConnection);
		transactionHandler.handleInitiativeTransaction(msg, isSync);
	}

	@Override
	public void handlePassiveTransaction(SocketHandler socketHandler,
			boolean isSync) throws DhtException {
		ITransactionHandler transactionHandler = new ClientTransactionHandlerImpl(
				localInfo, socketHandler, this, tcpConnection);
		transactionHandler.handlePassiveTransaction(isSync);
	}

	@Override
	public void setRouteTable(IClientRouteTable routeTable) {
		this.routeTable = routeTable;
	}

	private NodeInfo getServer(String fileName) throws DhtException {
		// TODO improvement needed
		Vector<NodeInfo> serverInfoList = routeTable.getServerList(fileName);
		if (serverInfoList.size() == 0) {
			updateRouteTable();
			do {
				serverInfoList = routeTable.getServerList(fileName);
				System.out.println("waiting for table");
				try {
					Thread.sleep(100);
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			} while (serverInfoList.size() == 0);
		}
		return serverInfoList.get(0);
	}

	private void updateRouteTable() throws DhtException {
		Message msg = new Message(IdGenerator.genTransactionId(),
				MessageType.CLIENT_TABLE_REQ, localInfo);
		NodeInfo serverInfo = routeTable.getMasterInfo();
		handleInitiativeTransaction(serverInfo, msg, true);
	}

}
